#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" tack.lib.smtptools - a lightweight wrapper around smtplib.

You want to use the send_message function like so:

    >>> from minimock import Mock
    >>> smtplib.SMTP = Mock('SMTP')
    >>> smtplib.SMTP.mock_returns = Mock('smtp_connection')
    >>> from tack.lib.smtptools import send_message
    >>> msg = message('fromyou@corp.com', 'tosomeuser@example.com', 'Hello, world', 'Free offers!  Great deals!')
    >>> send_message('fromyou@corp.com', [('tosomeuser@example.com', msg)], 'smtp.mailcorp.com')
    Called SMTP('smtp.mailcorp.com')
    Called smtp_connection.sendmail(
        'fromyou@corp.com',
        ['tosomeuser@example.com'],
        'Content-Type: text/plain; charset="us-ascii"\\nMIME-Version: 1.0\\nContent-Transfer-Encoding: 7bit\\nFrom: fromyou@corp.com\\nTo: tosomeuser@example.com\\nSubject: Hello, world\\n\\nFree offers!  Great deals!')
    [('tosomeuser@example.com', True)]
    >>> msg = message('fromyou@corp.com', 'tosomeuser@example.com', 'Hello, world', 'Free offers!  Great deals!','<b>some <i>html</i> text</b>')
    >>> send_message('fromyou@corp.com', [('tosomeuser@example.com', msg)], 'smtp.mailcorp.com')
    Called SMTP('smtp.mailcorp.com')
    Called smtp_connection.sendmail(...)
    [('tosomeuser@example.com', True)]

"""
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# Not using from ... import ... syntax because objecting mock'ing does not appear to work with it.
import smtplib
from socket import getdefaulttimeout, setdefaulttimeout


def message(sender, receiver, subject, text, html=None):
    """ Creates and returns new MIME message.

    @param sender address of message sender
    @param receiver address of message recipient
    @param subject message subject
    @param text plain text message
    @param html optional HTML markup for message; default is None
    @return MIMEText or MIMEMultipart instance
    """
    if html:
        return html_message(sender, receiver, subject, text, html)
    return text_message(sender, receiver, subject, text)


def text_message(sender, receiver, subject, text):
    """ Creates and returns new plain text MIME message.

    @param sender address of message sender
    @param receiver address of message recipient
    @param subject message subject
    @param text plain text message
    @return MIMEText instance
    """
    msg = MIMEText(text)
    msg['From'], msg['To'], msg['Subject'] = sender, receiver, subject    
    return msg


def html_message(sender, receiver, subject, text, html):
    """ Creates and returns new HTML MIME message.
    
    @param sender address of message sender
    @param receiver address of message recipient
    @param subject message subject
    @param text plain text message
    @param html optional HTML markup for message
    @return MIMEText or MIMEMultipart instance
    """
    msg = MIMEMultipart('related')
    msg['From'],  msg['To'], msg['Subject'] = sender, receiver, subject
    msg.preamble = 'This is a multi-part message in MIME format.'
    alt = MIMEMultipart('alternative')
    msg.attach(alt)
    alt.attach(MIMEText(text))
    alt.attach(MIMEText(html, 'html'))
    return msg


def send_message(sender, targets, mailhost=None, **options):
    """ Sends email to each recipient.

    @param sender mail account used to send message(s)
    @param targets list of (recipient, message) two-tuples
    @param mailhost host name of mail server; can optionally include port number
    @keyparam timeout optional socket timeout value; default is 10 seconds
    @keyparam username optional username for mail server authentication
    @keyparam password optional password for mail server authentication
    @return list of (rcpt, status) tuples
    """
    timeout = options.get('timeout', 10)
    prevtimeout = getdefaulttimeout()
    setdefaulttimeout(timeout)
    try:
        return send_message_real(sender, targets, mailhost, **options)
    finally:
        setdefaulttimeout(prevtimeout)


def send_message_real(sender, targets, mailhost=None, **options):
    """ Internal mail sending function.  Use the wrapper above instead.

    (And see the wrapper doc string, too.)
    """
    server = smtplib.SMTP(mailhost)
    username, password = options.get('username'), options.get('password')
    if username and password:
        server.login(username, password)
    def send(who, msg):
        try:
            server.sendmail(sender, [who], msg.as_string())
            return (who, True)
        except:
            return (who, False)
    return [send(target, message) for target, message in targets]


if __name__ == '__main__':
    sender = '"Message Sender" <tmelhase@ska-corp.com>'
    authuser = raw_input('Username: ')
    authpass = raw_input('Password: ')
    
    print 'Results:  ', \
          send_message(sender,
                      [('tmelhase@ska-corp.com', message(sender, 'tmelhase@ska-corp.com', 'Test Msg.', 'test body', '<b>Fu bar <i>baz</i></b>')),
                        ('troy.melhase@gmail.com', message(sender, 'troy.melhase@gmail.com', 'Test Msg.', 'test body', 'Other <b>tags</b>.'))
                       ],
                      'mail.sktcorp.com:25',
                      username=authuser,
                      password=authpass
                      )
